/*
	Copyright (C) 2011 Salil Bhagurkar

	This file is part of illusion

	illusion is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as
	published by the Free Software Foundation, either version 3 of
	the License, or (at your option) any later version.

	illusion is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with illusion. 
	If not, see <http://www.gnu.org/licenses/>.
*/

#include <arch/power.h>
#include <klib/lib.h>
#include <klib/memory.h>
#include <klib/string.h>
#include <kernel/console.h>
#include <kernel/fs.h>
#include <kernel/errors.h>
#include <kernel/kmalloc.h>

static void dev_read(struct vfs_node *dev, void *buf, u32 sector, u32 count)
{
	vfs_read(dev, "data", buf, sector, count);
}

static void dev_write(struct vfs_node *dev, void *buf, u32 sector, u32 count)
{
	vfs_write(dev, "data", buf, sector, count);
}

struct fat_bpb_12_16 {
	u8 drive_nr;
	u8 reserved;
	u8 boot_sig;
	u32 volume_id;
	u8 volume_label[11];
	u8 file_system_type[8];
}__attribute__((packed));

struct fat_bpb_32 {
	u32 fat_size_32;
	u16 extended_flags;
	u16 fs_version;
	u32 root_cluster;
	u16 fs_info;
	u16 backup_boot_sector;
	u8 reserved[12];
	u8 drive_nr;
	u8 reserved1;
	u8 boot_sig;
	u32 volume_id;
	u8 volume_label[11];
	u8 file_system_type[8];
}__attribute__((packed));

struct fat_bpb {
	u8 jmp_boot[3];
	u8 oem_name[8];
	u16 bytes_per_sector;
	u8 sectors_per_cluster;
	u16 reserved_sector_count;
	u8 nr_fats;
	u16 root_ent_count;
	u16 total_sectors_16;
	u8 media;
	u16 fat_size_16;
	u16 sectors_per_track;
	u16 nr_heads;
	u32 hidden_sectors;
	u32 total_sectors_32;
	union {
		struct fat_bpb_12_16 fat_bpb_12_16;
		struct fat_bpb_32 fat_bpb_32;
	}spec;
}__attribute__((packed));


struct fat_dirent {
	u8 name[11];
	u8 attr;
	u8 nt_reserved;
	u8 crt_time_tenth;
	u16 crt_time;
	u16 crt_date;
	u16 last_accessed_date;
	u16 first_cluster_high;
	u16 write_time;
	u16 write_date;
	u16 first_cluster_low;
	u32 file_size;
}__attribute__((packed));

//This structure is assigned to each file
struct fat_file_info {
	struct fat_bpb *fat_bpb;
	struct fat_dirent *dirent;
	//The index of the directory entry of this file/directory
	u32 dirent_index;
};

#define ATTR_READ_ONLY 1
#define ATTR_HIDDEN 2
#define ATTR_SYSTEM 4
#define ATTR_VOLUME_ID 8
#define ATTR_DIRECTORY 0x10
#define ATTR_ARCHIVE 0x20

#define ATTR_LONG_NAME_MASK (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)


static u32 get_root_dir_sector_count(struct fat_bpb *fat_bpb)
{
	return ((fat_bpb->root_ent_count * 32) + (fat_bpb->bytes_per_sector - 1)) / fat_bpb->bytes_per_sector;
}

static u32 get_fat_size(struct fat_bpb *fat_bpb)
{
	if(fat_bpb->fat_size_16 != 0)
		return fat_bpb->fat_size_16;
	else
		return fat_bpb->spec.fat_bpb_32.fat_size_32;
}

static u32 get_total_sectors(struct fat_bpb *fat_bpb)
{
	if(fat_bpb->total_sectors_16 != 0)
		return fat_bpb->total_sectors_16;
	else
		return fat_bpb->total_sectors_32;
}

static u32 get_first_data_sector(struct fat_bpb *fat_bpb)
{
	u32 fat_sz = get_fat_size(fat_bpb);
	return fat_bpb->reserved_sector_count + (fat_bpb->nr_fats * fat_sz)
			+ get_root_dir_sector_count(fat_bpb);
}

static u32 get_first_sector_of_cluster(struct fat_bpb *fat_bpb, u32 cluster)
{
	return ((cluster - 2) * fat_bpb->sectors_per_cluster) + get_first_data_sector(fat_bpb);
}

static void *read_cluster(struct fat_bpb *fat_bpb, struct vfs_node *dev, u32 cluster)
{
	u32 sector = get_first_sector_of_cluster(fat_bpb, cluster);
	void *buf = kmalloc(fat_bpb->bytes_per_sector * fat_bpb->sectors_per_cluster, "dev");
	dev_read(dev, buf, sector, fat_bpb->sectors_per_cluster);
	return buf;
}

static void write_cluster(struct fat_bpb *fat_bpb, struct vfs_node *dev, u32 cluster, void *buf)
{
	u32 sector = get_first_sector_of_cluster(fat_bpb, cluster);
	dev_write(dev, buf, sector, fat_bpb->sectors_per_cluster);
}

static u8 get_fat_type(struct fat_bpb *fat_bpb)
{
	u32 fat_sz = get_fat_size(fat_bpb);
	u32 total_sect = get_total_sectors(fat_bpb);
	u32 data_sectors = total_sect -
		(fat_bpb->reserved_sector_count + (fat_bpb->nr_fats * fat_sz) +
				get_root_dir_sector_count(fat_bpb));
	u32 nr_clusters = data_sectors / fat_bpb->sectors_per_cluster;
	if(nr_clusters < 4085)
		return 12;
	else if(nr_clusters < 65525)
		return 16;
	else
		return 32;
}

static void get_cluster_entry_params(struct fat_bpb *fat_bpb, u32 cluster,
		u32 *fat_sector, u32 *fat_entry_offset, u8 *fat_type)
{
	*fat_type = get_fat_type(fat_bpb);
	u32 fat_offset;
	if(*fat_type == 12)
		fat_offset = cluster + (cluster / 2);
	else if(*fat_type == 16)
		fat_offset = cluster * 2;
	else if(*fat_type == 32)
		fat_offset = cluster * 4;
	*fat_sector = fat_bpb->reserved_sector_count
			+(fat_offset / fat_bpb->bytes_per_sector);
	*fat_entry_offset = fat_offset % fat_bpb->bytes_per_sector;
}

static u32 get_cluster_entry(struct fat_bpb *fat_bpb, struct vfs_node *dev, u32 cluster)
{
	u32 fat_sector, fat_entry_offset;
	u8 fat_type;
	get_cluster_entry_params(fat_bpb, cluster, &fat_sector, &fat_entry_offset, &fat_type);
	u8 *sector_buf;
	if(fat_type == 12 && fat_entry_offset == (fat_bpb->bytes_per_sector - 1)) {
		sector_buf = (u8 *)kmalloc(fat_bpb->bytes_per_sector * 2, "dev");
		dev_read(dev, sector_buf, fat_sector, 2);
	} else {
		sector_buf = (u8 *)kmalloc(fat_bpb->bytes_per_sector, "dev");
		dev_read(dev, sector_buf, fat_sector, 1);
	}

	u32 entry;
	if(fat_type == 12) {
		entry = *((u16 *)&sector_buf[fat_entry_offset]);
		if(cluster & 1)
			entry = entry >> 4;
		else
			entry = entry & 0x0FFF;

	} else if(fat_type == 16)
		entry = *((u16 *)&sector_buf[fat_entry_offset]);
	else if(fat_type == 32)
		entry = (*((u32 *)&sector_buf[fat_entry_offset])) & 0x0FFFFFFF;

	if(fat_type == 12 && fat_entry_offset == (fat_bpb->bytes_per_sector - 1)) {
		kfree(sector_buf, fat_bpb->bytes_per_sector * 2, "dev");
	} else {
		kfree(sector_buf, fat_bpb->bytes_per_sector, "dev");
	}

	return entry;
}

static void set_cluster_entry(struct fat_bpb *fat_bpb, struct vfs_node *dev, u32 cluster, u32 entry)
{
	u32 fat_sector, fat_entry_offset;
	u8 fat_type;
	get_cluster_entry_params(fat_bpb, cluster, &fat_sector, &fat_entry_offset, &fat_type);
	u8 *sector_buf;
	if(fat_type == 12 && fat_entry_offset == (fat_bpb->bytes_per_sector - 1)) {
		sector_buf = (u8 *)kmalloc(fat_bpb->bytes_per_sector * 2, "dev");
		dev_read(dev, sector_buf, fat_sector, 2);
	} else {
		sector_buf = (u8 *)kmalloc(fat_bpb->bytes_per_sector, "dev");
		dev_read(dev, sector_buf, fat_sector, 1);
	}

	if(fat_type == 12) {
		if(cluster & 1) {
			entry = entry << 4;
			*((u16 *)&sector_buf[fat_entry_offset])
					= (*((u16 *)&sector_buf[fat_entry_offset])) & 0x000F;
		} else {
			entry = entry & 0x0FFF;
			*((u16 *)&sector_buf[fat_entry_offset])
					= (*((u16 *)&sector_buf[fat_entry_offset])) & 0xF000;
		}
		*((u16 *)&sector_buf[fat_entry_offset]) =
			(*((u16 *)&sector_buf[fat_entry_offset])) | entry;
	} else if(fat_type == 16)
		*((u16 *)&sector_buf[fat_entry_offset]) = entry;
	else {
		entry &= 0x0FFFFFFF;
		*((u32 *)&sector_buf[fat_entry_offset]) = (*((u32 *)&sector_buf[fat_entry_offset])) & 0xF0000000;
		*((u32 *)&sector_buf[fat_entry_offset]) = (*((u32 *)&sector_buf[fat_entry_offset])) | entry;
	}

	if(fat_type == 12 && fat_entry_offset == (fat_bpb->bytes_per_sector - 1)) {
		dev_write(dev, sector_buf, fat_sector, 2);
		kfree(sector_buf, fat_bpb->bytes_per_sector * 2, "dev");
	} else {
		dev_write(dev, sector_buf, fat_sector, 1);
		kfree(sector_buf, fat_bpb->bytes_per_sector, "dev");
	}
}

static int is_eoc(u8 fat_type, u32 entry)
{
	if(fat_type == 12) {
		if(entry >= 0x0FF8)
			return 1;
	} else if(fat_type == 16) {
		if(entry >= 0xFFF8)
			return 1;
	} else if(fat_type == 32) {
		if(entry >= 0x0FFFFFF8)
			return 1;
	}
	return 0;
}

static void string_to_lower(char *src)
{
	while((*src) != '\0') {
		char c = *src;
		if(c >= 'A' && c <= 'Z')
			*src = c - 'A' + 'a';
		src++;
	}
}

/*Decode stupid 8.3 to sane names with 13 characters including a dot and a null*/
static char *decode_fat_name(char *name_dest, char *name_src)
{
	unsigned i = 0;
	while(name_src[i] != 0x20) {
		name_dest[i] = name_src[i];
		i++;
		if(i >= 8)
			break;
	}
	if(name_src[8] != 0x20) {
		name_dest[i++] = '.';
		for(int j = 8; j <= 10; j++) {
			if(name_src[j] != 0x20)
				name_dest[i++] = name_src[j];
			else
				break;
		}
		name_dest[i++] = 0;
	} else
		name_dest[i++] = 0;
	string_to_lower(name_dest);
	return name_dest;
}



static u32 get_cluster_size(struct fat_bpb *fat_bpb)
{
	return fat_bpb->bytes_per_sector * fat_bpb->sectors_per_cluster;
}

static void *read_virtual_cluster(struct fat_bpb *fat_bpb, struct vfs_node *dev, u32 cluster, u32 cluster_offset)
{
	u8 fat_type = get_fat_type(fat_bpb);
	while(cluster_offset--) {
		cluster = get_cluster_entry(fat_bpb, dev, cluster);
		if(is_eoc(fat_type, cluster))
			return null;
	}
	return read_cluster(fat_bpb, dev, cluster);
}

static struct fat_dirent *read_dirent(struct fat_bpb *fat_bpb, struct vfs_node *dev, u32 dirent_index, u32 first_cluster)
{
	u32 dirent_offset = dirent_index * sizeof(struct fat_dirent);
	u32 v_cluster = dirent_offset / get_cluster_size(fat_bpb);
	dirent_offset -= v_cluster * get_cluster_size(fat_bpb);
	void *buf = read_virtual_cluster(fat_bpb, dev, first_cluster, v_cluster);
	if(!buf)
		return null;
	struct fat_dirent *dirent = (struct fat_dirent *)kmalloc(sizeof(struct fat_dirent), "dev");
	memcpy(dirent, buf + dirent_offset, sizeof(struct fat_dirent));
	kfree(buf, get_cluster_size(fat_bpb), "dev");
	return dirent;
}

static struct fat_dirent *read_1216_root_dirent(struct fat_bpb *fat_bpb, struct vfs_node *dev, u32 dirent_index)
{
	u32 root_sect = fat_bpb->reserved_sector_count + fat_bpb->nr_fats * fat_bpb->fat_size_16;
	u32 nr_sect = (fat_bpb->root_ent_count) * sizeof(struct fat_dirent) / (fat_bpb->bytes_per_sector);
	void *buf = kmalloc(nr_sect * fat_bpb->bytes_per_sector, "dev");
	dev_read(dev, buf, root_sect, nr_sect);
	struct fat_dirent *dirent = (struct fat_dirent *)kmalloc(sizeof(struct fat_dirent), "dev");
	memcpy(dirent, buf + dirent_index * sizeof(struct fat_dirent), sizeof(struct fat_dirent));
	kfree(buf, nr_sect * fat_bpb->bytes_per_sector, "dev");
	return dirent;
}

static struct fat_dirent *read_root_dirent(struct fat_bpb *fat_bpb, struct vfs_node *dev, u32 dirent_index)
{
	u8 fat_type = get_fat_type(fat_bpb);
	if(fat_type == 12 || fat_type == 16)
		return read_1216_root_dirent(fat_bpb, dev, dirent_index);
	else
		return read_dirent(fat_bpb, dev, dirent_index, fat_bpb->spec.fat_bpb_32.root_cluster);
}

struct fat_bpb *__fatfs_detect(struct vfs_node *dev)
{
	char *buf = (char *)kmalloc(512, "dev");
	dev_read(dev, buf, 0, 1);
	struct fat_bpb *fat_bpb = (struct fat_bpb *)buf;
	u8 fat_type = get_fat_type(fat_bpb);
	int result = 0;
	if(fat_type == 12 || fat_type == 16) {
		if(memeq((char *)fat_bpb->spec.fat_bpb_12_16.file_system_type,
				"FAT", 3)) {
			result = 1;
		}
	} else {
		if(memeq((char *)fat_bpb->spec.fat_bpb_32.file_system_type,
				"FAT", 3)) {
			result = 1;
		}
	}
	if(result) {
		debug("FAT%u detected on %s\n", fat_type, dev->name);
		return fat_bpb;
	} else {
		kfree(buf, 512, "dev");
		return null;
	}
}

static int fatfs_detect(struct vfs_node *dev)
{
	struct fat_bpb *fat_bpb = __fatfs_detect(dev);
	if(fat_bpb) {
		kfree(fat_bpb, 512, "dev");
		return 0;
	}
	return ENOSUP;
}

struct fat_file_info *new_fat_file_info(struct fat_dirent *dirent,
		struct fat_bpb *fat_bpb, u32 dirent_index)
{
	struct fat_file_info *f_info = (struct fat_file_info *)kmalloc(sizeof(struct fat_file_info), "dev");
	f_info->dirent = dirent;
	f_info->fat_bpb = fat_bpb;
	f_info->dirent_index = dirent_index;
	return f_info;
}

static struct fat_dirent *next_valid_root_dirent(struct fat_bpb *fat_bpb,
		struct vfs_node *dev, int *index);
static struct fat_dirent *next_valid_dirent(struct fat_bpb *fat_bpb,
		struct vfs_node *dev, u32 dir_cluster, int *index);

static int fatfs_sync_node(struct vfs_node *dev, struct vfs_node *node)
{
	//Check for a lookup
	if((node->flags & VFS_SYNC_DEV) && !(node->class)) {
		if(node->parent->flags & VFS_IS_ROOT) {
			struct fat_bpb *fat_bpb = (struct fat_bpb *)node->parent->class;

			int index = 0;

			struct fat_dirent *dirent;
			do {
				dirent = next_valid_root_dirent(fat_bpb, dev, &index);
				if(dirent) {
					char name[13];
					decode_fat_name(name, (char *)dirent->name);
					if(streq(name, node->name)) {
						node->length = dirent->file_size;
						node->class = new_fat_file_info(dirent, fat_bpb, index);
						return 0;
					}
					kfree(dirent, sizeof(struct fat_dirent), "dev");
				}
				index++;
			} while(dirent);
			return ENOENT;
		} else {
			struct fat_file_info *f_info = (struct fat_file_info *)node->parent->class;
			u32 cluster = (f_info->dirent->first_cluster_high << 16) | f_info->dirent->first_cluster_low;

			int index = 0;

			struct fat_dirent *dirent;
			do {
				dirent = next_valid_dirent(f_info->fat_bpb, dev, cluster, &index);
				if(dirent) {
					char name[13];
					decode_fat_name(name, (char *)dirent->name);
					if(streq(name, node->name)) {
						node->length = dirent->file_size;
						node->class = new_fat_file_info(dirent, f_info->fat_bpb, index);
						return 0;
					}
					kfree(dirent, sizeof(struct fat_dirent), "dev");
				}
				index++;
			} while(dirent);
			return ENOENT;
		}
	}
	return EPENDSYNC;
}

static int fatfs_sync_page(struct vfs_node *dev, struct vfs_node *node, struct vfs_page *page)
{
	struct fat_file_info *f_info = (struct fat_file_info *)node->class;
	u32 base_cluster = (f_info->dirent->first_cluster_high << 16) | (f_info->dirent->first_cluster_low);
	u32 cluster_size = get_cluster_size(f_info->fat_bpb);
	if(page->flags & VFS_SYNC_DEV) {
		u32 start_cluster = (page->start) / cluster_size;
		u32 start_cluster_offset = page->start - start_cluster * cluster_size;
		u32 page_end = page->start + page->length;
		u32 end_cluster = page_end / cluster_size;
		u32 end_cluster_offset = page_end - end_cluster * cluster_size;
		int j;
		//Read the first cluster (possibly partially)
		void *clus_buf = read_virtual_cluster(f_info->fat_bpb, dev, base_cluster, start_cluster);
		memcpy(page->page_data, clus_buf + start_cluster_offset, cluster_size - start_cluster_offset);
		kfree(clus_buf, cluster_size, "dev");
		if(end_cluster - start_cluster > 2) {
			u32 cluster;
			void *buf = page->page_data + (cluster_size - start_cluster_offset);
			for(cluster = start_cluster + 1; cluster < end_cluster; cluster++) {
				void *clus_buf = read_virtual_cluster(f_info->fat_bpb, dev, base_cluster, cluster);
				memcpy(buf, clus_buf, cluster_size);
				kfree(clus_buf, cluster_size, "dev");
				buf += cluster_size;
			}
		}
		if(end_cluster != start_cluster) {
			void *buf = page->page_data + (end_cluster - start_cluster) * cluster_size;
			void *clus_buf = read_virtual_cluster(f_info->fat_bpb, dev, base_cluster, end_cluster);
			memcpy(buf, clus_buf, end_cluster_offset);
			kfree(clus_buf, cluster_size, "dev");
		}
		return 0;
	} else {
		return EPENDSYNC;
	}
}

static int is_legal_name(struct fat_dirent *test)
{
	unsigned i;
	for(i = 0;i < 11; i++)	{
		if(test->name[i] < 0x20 || test->name[i] == 0x22 ||
		test->name[i] == 0x2a || test->name[i] == 0x2b || test->name[i] == 0x2c ||
		test->name[i] == 0x2e || test->name[i] == 0x2f || test->name[i] == 0x3a ||
		test->name[i] == 0x3b || test->name[i] == 0x3c || test->name[i] == 0x3d ||
		test->name[i] == 0x3e || test->name[i] == 0x3f || test->name[i] == 0x5b ||
		test->name[i] == 0x5c || test->name[i] == 0x5d || test->name[i] == 0x7c ||
		test->name[i] == 0xe5)
			return 0;
	}
	return 1;
}

/*
 * Searches for valid directory entries including the one at the passed index
 */
static struct fat_dirent *next_valid_root_dirent(struct fat_bpb *fat_bpb,
		struct vfs_node *dev, int *index)
{
	int i = *index;
	for( ; ; i++) {
		struct fat_dirent *dirent = read_root_dirent(fat_bpb, dev, i);
		if(dirent->name[0] == '\0') {
			kfree(dirent, sizeof(struct fat_dirent), "dev");
			return null;
		}
		if(is_legal_name(dirent) && !(dirent->attr & ATTR_LONG_NAME_MASK)
				&& !(dirent->attr & ATTR_VOLUME_ID)) {
			*index = i;
			return dirent;
		}
		kfree(dirent, sizeof(struct fat_dirent), "dev");
	}
	return null;
}

static struct fat_dirent *next_valid_dirent(struct fat_bpb *fat_bpb,
		struct vfs_node *dev, u32 dir_cluster, int *index)
{
	int i = *index;
	for( ; ; i++) {
		struct fat_dirent *dirent = read_dirent(fat_bpb, dev, i, dir_cluster);
		if(dirent->name[0] == '\0') {
			kfree(dirent, sizeof(struct fat_dirent), "dev");
			return null;
		}
		if(is_legal_name(dirent) && !(dirent->attr & ATTR_LONG_NAME_MASK)
				&& !(dirent->attr & ATTR_VOLUME_ID)) {
			*index = i;
			return dirent;
		}
		kfree(dirent, sizeof(struct fat_dirent), "dev");
	}
	return null;
}

static struct vfs_node *fatfs_get_child(struct vfs_node *dev, struct vfs_node *node)
{
	if(node->flags & VFS_IS_ROOT) {
		struct fat_bpb *fat_bpb = (struct fat_bpb *)node->class;

		int index = 0;
		struct fat_dirent *dirent = next_valid_root_dirent(fat_bpb, dev, &index);
		if(!dirent)
			return null;

		char name[13];
		decode_fat_name(name, (char *)dirent->name);

		//Plug into the VFS
		struct vfs_node *new = vfs_create_raw_node(node, name);
		new->length = dirent->file_size;
		new->class = new_fat_file_info(dirent, fat_bpb, index);
		return new;
	} else {
		struct fat_file_info *fat_file_info = (struct fat_file_info *)node->class;
		struct fat_dirent *dirent = fat_file_info->dirent;
		if(!(dirent->attr & ATTR_DIRECTORY)) {
			console_printf("fatfs: That is not a directory :P\n");
			return null;
		}

		u32 dir_cluster = (dirent->first_cluster_high << 16) | dirent->first_cluster_low;
		int index = 0;
		dirent = next_valid_dirent(fat_file_info->fat_bpb, dev, dir_cluster, &index);
		if(!dirent)
			return null;

		char name[13];
		decode_fat_name(name, (char *)dirent->name);

		//Plug into the VFS
		struct vfs_node *new = vfs_create_raw_node(node, name);
		new->length = dirent->file_size;
		new->class = new_fat_file_info(dirent, fat_file_info->fat_bpb, index);
		return new;
	}
}

static struct vfs_node *fatfs_next_child(struct vfs_node *dev, struct vfs_node *node)
{
	if(node->parent->flags & VFS_IS_ROOT) {
		//Now we have the fat_file_info to parse
		struct fat_file_info *f_info = (struct fat_file_info *)node->class;

		//Move to next index
		int index = f_info->dirent_index + 1;
		struct fat_dirent *dirent = next_valid_root_dirent(f_info->fat_bpb, dev, &index);
		if(!dirent)
			return null;

		char name[13];
		decode_fat_name(name, (char *)dirent->name);

		//Plug into the VFS
		struct vfs_node *new = vfs_create_raw_node(node, name);
		new->length = dirent->file_size;
		new->class = new_fat_file_info(dirent, f_info->fat_bpb, index);
		return new;
	} else {
		struct fat_file_info *fat_file_info = (struct fat_file_info *)node->parent->class;
		struct fat_dirent *dirent = fat_file_info->dirent;
		if(!(dirent->attr & ATTR_DIRECTORY)) {
			console_printf("fatfs: That is not a directory :P\n");
			return null;
		}

		struct fat_file_info *prev_f_info = (struct fat_file_info *)node->class;

		u32 dir_cluster = (dirent->first_cluster_high << 16) | dirent->first_cluster_low;
		//Start with the next one
		int index = prev_f_info->dirent_index + 1;
		dirent = next_valid_dirent(prev_f_info->fat_bpb, dev, dir_cluster, &index);
		if(!dirent)
			return null;

		char name[13];
		decode_fat_name(name, (char *)dirent->name);

		//Plug into the VFS
		struct vfs_node *new = vfs_create_raw_node(node, name);
		new->length = dirent->file_size;
		new->class = new_fat_file_info(dirent, prev_f_info->fat_bpb, index);
		return new;
	}
}

static int fatfs_mount(struct vfs_node *dev, struct vfs_node *on)
{
	struct fat_bpb *fat_bpb = __fatfs_detect(dev);
	if(!fat_bpb)
		return ESTINV;
	on->class = fat_bpb;
	return 0;
}

static int fatfs_unmount(struct vfs_node *dev, struct vfs_node *from)
{
	return 0;
}

static struct fs_driver fatfs_driver = {
	.name = "fatfs",
	.detect_fs = fatfs_detect,
	.next = null,
	.prev = null,
	.mount_fs = fatfs_mount,
	.unmount_fs = fatfs_unmount,
	.sync_node = fatfs_sync_node,
	.sync_page = fatfs_sync_page,
	.get_child = fatfs_get_child,
	.next_child = fatfs_next_child,
};

int fatfs_init()
{
	register_fs_driver(&fatfs_driver);
	return 0;
}
